package org.rcgwt.client.ui.grid.renderer;

import org.rcgwt.client.theme.CssStyle;
import org.rcgwt.client.ui.grid.internal.ViewRenderContextImpl;
import org.rcgwt.client.ui.grid.internal.model.IElementModel;
import org.rcgwt.client.ui.grid.internal.model.IElementModelDelta;
import org.rcgwt.client.ui.grid.internal.model.IGridModel;
import org.rcgwt.client.ui.grid.internal.model.IViewModel;
import org.rcgwt.client.ui.grid.internal.model.IViewModelElements;
import org.rcgwt.client.ui.grid.model.IColumn;
import org.rcgwt.client.ui.grid.model.IGridWidget;
import org.rcgwt.client.ui.grid.provider.IColumnProvider;
import org.rcgwt.client.ui.grid.theme.IGridTheme;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style;

/**
 * 
 * @author Olivier Oeuillot (latest modification by $Author: $)
 * @version $Revision: $ $Date: $
 */
public class DefaultGridRenderer implements IGridRenderer {

	@Override
	public IGridRenderContext createRenderContext(IGridModel gridModel) {
		return new ViewRenderContextImpl(gridModel);
	}

	@Override
	public Element renderRoot(IGridRenderContext gridRenderContext,
			Document document) {

		return document.createDivElement();
	}

	public void postRenderRoot(IGridRenderContext gridRenderContext,
			Element gridElement) {

		IGridModel gridModel = gridRenderContext.getGridModel();

		IGridWidget grid = gridModel.getGridWidget();

		IGridTheme gridTheme = gridRenderContext.getTheme();

		updateStateMask(gridRenderContext, grid.getElement(), gridModel);

		Style rootStyle = gridElement.getStyle();

		int gridBorderWidth = gridTheme.getGridBorderWidth();

		rootStyle.setPropertyPx(Styles.BORDER_WIDTH, gridBorderWidth);

		rootStyle.setPropertyPx(Styles.WIDTH, grid.getWidth());
		rootStyle.setPropertyPx(Styles.HEIGHT, grid.getHeight());
	}

	@Override
	public void render(IGridRenderContext gridRenderContext, Element gridElement) {

		IGridTheme theme = gridRenderContext.getTheme();

		IGridModel gridModel = gridRenderContext.getGridModel();

		int viewModelIndex = 0;
		for (IViewModel viewModel : gridModel.listViewModels()) {
			IViewRenderContext viewRenderContext = gridRenderContext
					.getViewRenderContext(viewModel);

			IViewRenderer viewRenderer = theme.getViewRenderer(viewModel);

			Element viewElement = viewRenderer.render(viewRenderContext,
					gridElement, viewModel, viewModelIndex++);

			gridElement.appendChild(viewElement);
		}

		updateLayout(gridRenderContext);
	}

	@Override
	public void update(IGridRenderContext gridRenderContext, Element gridElement) {

		IGridTheme theme = gridRenderContext.getTheme();

		IGridModel gridModel = gridRenderContext.getGridModel();

		int viewModelIndex = 0;
		for (IViewModel viewModel : gridModel.listViewModels()) {

			IViewRenderContext viewRenderContext = gridRenderContext
					.getViewRenderContext(viewModel);

			IViewRenderer viewRenderer = theme.getViewRenderer(viewModel);

			Element viewElement = viewModel.getViewModelElements()
					.getViewElement();

			viewRenderer.update(viewRenderContext, viewElement, viewModel,
					viewModelIndex++);
		}
	}

	public void updateLayout(IGridRenderContext gridRenderContext) {

		IGridModel gridModel = gridRenderContext.getGridModel();
		IGridWidget grid = gridModel.getGridWidget();
		IGridTheme gridTheme = gridRenderContext.getTheme();

		int viewBorderWidth = gridTheme.getViewBorderWidth();

		int gridPaddings[] = gridTheme.getGridPaddings();

		int totalViewWidth = grid.getWidth() - gridPaddings[3]
				- gridPaddings[1] - gridTheme.getGridBorderWidth() * 2;

		int totalViewHeight = grid.getHeight() - gridPaddings[0]
				- gridPaddings[2] - gridTheme.getGridBorderWidth() * 2;

		int innerViewWidth = totalViewWidth - gridTheme.getViewBorderWidth()
				* 2;
		int innerViewHeight = totalViewHeight - gridTheme.getViewBorderWidth()
				* 2;

		int totalColumnsWidth = innerViewWidth;

		IColumnProvider columnProvider = grid.getColumnProvider();

		int count = columnProvider.getColumnCount();

		int columnSizes[] = new int[count];
		int pws[] = new int[count];
		int pcSize = 0;
		int totalColumnSize = 0;

		int columnModelIndexes[] = gridRenderContext.getColumnModelIndexes();
		for (int i = 0; i < columnModelIndexes.length; i++) {
			int columnModelIndex = columnModelIndexes[i];

			IColumn column = columnProvider
					.getColumnByModelIndex(columnModelIndex);

			int w = column.getWidth();
			if (w < 0) {
				if (totalColumnsWidth <= 0) {
					continue;
				}

				int pw = column.getPercentWidth();
				if (pw > 0) {
					pcSize += pw;
					pws[i] = pw;
				}

				continue;
			}

			totalColumnSize += w;
			columnSizes[i] = w;
		}

		if (pcSize > 0) {
			int pw = totalColumnsWidth - totalColumnSize;
			if (pw > 0) {
				double d = pw / (double) pcSize;

				for (int i = 0; i < pws.length; i++) {
					if (pws[i] <= 0) {
						continue;
					}

					int w = (int) (pws[i] * d);
					columnSizes[i] = w;
					totalColumnSize += w;
				}
			}
		}

		int sx = gridPaddings[3];
		int sy = gridPaddings[0];

		for (IViewModel viewModel : gridModel.listViewModels()) {
			IViewModelElements viewModelElements = viewModel
					.getViewModelElements();

			IViewRenderContext viewRenderContext = gridRenderContext
					.getViewRenderContext(viewModel);

			Element viewElement = viewModelElements.getViewElement();

			Style containerStyle = viewElement.getStyle();
			containerStyle.setPropertyPx(Styles.LEFT, sx);
			containerStyle.setPropertyPx(Styles.TOP, sy);
			containerStyle.setPropertyPx(Styles.WIDTH, totalViewWidth);
			containerStyle.setPropertyPx(Styles.HEIGHT, totalViewHeight);
			containerStyle.setPropertyPx(Styles.BORDER_WIDTH, viewBorderWidth);

			IViewRenderer viewRenderer = gridTheme.getViewRenderer(viewModel);

			viewRenderer.updateLayout(viewRenderContext, viewModel,
					viewElement, innerViewWidth, innerViewHeight,
					totalColumnSize, columnSizes);
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public void processDelta(IGridRenderContext gridRenderContext,
			Element gridElement,
			IElementModelDelta<IGridModel> modelElementDelta) {

		IGridTheme gridTheme = gridRenderContext.getTheme();

		if (modelElementDelta.hasStateMaskChanged()) {
			updateStateMask(gridRenderContext, gridElement, gridRenderContext
					.getGridModel());
		}

		IElementModelDelta<? extends IElementModel> deltas[] = modelElementDelta
				.listChildrenDeltas();

		IViewModel viewModels[] = gridRenderContext.getGridModel()
				.listViewModels();

		if (viewModels.length == 1) {
			// Only one view !

			IViewModel viewModel = viewModels[0];

			IViewRenderContext viewRenderContext = gridRenderContext
					.getViewRenderContext(viewModel);

			IViewRenderer viewRenderer = gridTheme.getViewRenderer(viewModel);

			Element viewElement = viewModel.getViewModelElements()
					.getViewElement();

			for (IElementModelDelta<? extends IElementModel> delta : deltas) {
				if (delta == null) {
					continue;
				}

				IElementModel elementModel = delta.getModelElement();
				if (elementModel.getType() == IElementModel.Type.VIEW) {
					IElementModelDelta<IViewModel> viewElementDelta = (IElementModelDelta<IViewModel>) delta;

					viewRenderer.processDelta(viewRenderContext, viewElement,
							viewElementDelta);
					continue;
				}

				viewRenderer.processSibblingDelta(viewRenderContext,
						viewElement, delta);
			}

			return;
		}

		// Multiple views !

		for (IElementModelDelta<? extends IElementModel> delta : deltas) {
			if (delta == null) {
				continue;
			}

			IElementModel elementModel = delta.getModelElement();
			if (elementModel.getType() == IElementModel.Type.VIEW) {

				// Evenement sur une vue

				IViewModel viewModel = (IViewModel) elementModel;
				IElementModelDelta<IViewModel> viewElementDelta = (IElementModelDelta<IViewModel>) delta;

				IViewRenderContext viewRenderContext = gridRenderContext
						.getViewRenderContext(viewModel);

				IViewRenderer viewRenderer = gridTheme
						.getViewRenderer(viewModel);

				Element viewElement = viewModel.getViewModelElements()
						.getViewElement();

				viewRenderer.processDelta(viewRenderContext, viewElement,
						viewElementDelta);
				continue;
			}

			// Un autre objet que la vue, que l'on itere sur une vue !

			for (IViewModel viewModel : viewModels) {

				IViewRenderContext viewRenderContext = gridRenderContext
						.getViewRenderContext(viewModel);

				IViewRenderer viewRenderer = gridTheme
						.getViewRenderer(viewModel);

				Element viewElement = viewModel.getViewModelElements()
						.getViewElement();

				viewRenderer.processSibblingDelta(viewRenderContext,
						viewElement, delta);
			}
		}
	}

	protected void updateStateMask(IGridRenderContext gridRenderContext,
			Element element, IGridModel gridModel) {

		IGridWidget grid = gridModel.getGridWidget();

		IGridTheme gridTheme = gridRenderContext.getTheme();

		grid.setClassName(gridTheme.getGridCssStyle(gridModel).getClassName());

		CssStyle themeCssStyle = getCssStyle(gridRenderContext, gridModel);
		if (themeCssStyle != null) {
			grid.addStyleName(themeCssStyle.getClassName());
		}
	}

	protected CssStyle getCssStyle(IGridRenderContext gridRenderContext,
			IGridModel gridModel) {

		IGridTheme gridTheme = gridRenderContext.getTheme();

		return gridTheme.getGridThemeCssStyle(gridModel);
	}

}
